/** sps_base64.cpp - implement of base64 encode/decode
 */

#include "sps_config.h"


static const char ETable[] =
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


static const char DTable[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  62, // '+'
  0, 0, 0,
  63, // '/'
  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9'
  0, 0, 0, 0, 0, 0, 0,
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
  13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'
  0, 0, 0, 0, 0, 0,
  26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
  39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'
};

/*
 * @note You can remove the comments in this function
 *  if you want to include newline and carriage return
 *  in your base64 encode string.
 */
std::string Base64Encode(const std::string& str)
{
  unsigned char      tmp[4] = {0};
  std::string        es;
  unsigned char     *data = (unsigned char *)str.c_str();
  size_t             len = str.length();
  //  size_t             newline = 0;

  for (size_t i = 0; i < (size_t)(len / 3); i++) {
    tmp[1] = *data++;
    tmp[2] = *data++;
    tmp[3] = *data++;

    es += ETable[tmp[1] >> 2];
    es += ETable[((tmp[1] << 4) | (tmp[2] >> 4)) & 0x3f];
    es += ETable[((tmp[2] << 2) | (tmp[3] >> 6)) & 0x3f];
    es += ETable[tmp[3] & 0x3f];
    /*
    if (newline += 4, newline == 76) {
      es += "\r\n";
      newline = 0;
    }
    */
  }

  size_t mod = (size_t)len % 3;

  if (mod == 1) {
    tmp[1] = *data++;
    es += ETable[(tmp[1] & 0xfc) >> 2];
    es += ETable[((tmp[1] & 0x03) << 4)];
    es += "==";
  } else if (mod == 2) {
    tmp[1] = *data++;
    tmp[2] = *data++;

    es += ETable[(tmp[1] & 0xfc) >> 2];
    es += ETable[((tmp[1] & 0x03) << 4) | ((tmp[2] & 0xf0) >> 4)];
    es += ETable[((tmp[2] & 0x0f) << 2)];
    es += "=";
  }

  return es;
}

std::string Base64Decode(const std::string& str)
{
  int            v = 0;
  size_t         i = 0;
  size_t         len = str.length();
  unsigned char *data = (unsigned char *)str.c_str();
  std::string    ds;

  while (i < len) {
    if (*data != '\r' && *data != '\n') {
      v = DTable[*data++] << 18;
      v += DTable[*data++] << 12;
      ds += (v & 0x00FF0000) >> 16;

      if (*data != '=') {
	v += DTable[*data++] << 6;
	ds += (v & 0x0000FF00) >> 8;

	if (*data != '=') {
	  v += DTable[*data++];
	  ds += (v & 0x000000FF);
	}
      }

      i += 4;
      
    } else {  /* skip the newline and carriage return */
      data++;
      i++;
    }
  }

  return ds;
}
  
